//package org.jscc.app.client.biojava3.core.sequence.location;
//
//import java.io.IOException;
//import java.io.Reader;
//import java.io.StringReader;
//import java.util.ArrayList;
//import java.util.List;
//import java.util.regex.Matcher;
//import java.util.regex.Pattern;
//
//import org.jscc.app.client.biojava3.core.exceptions.ParserException;
//import org.jscc.app.client.biojava3.core.sequence.AccessionID;
//import org.jscc.app.client.biojava3.core.sequence.DataSource;
//import org.jscc.app.client.biojava3.core.sequence.Strand;
//import org.jscc.app.client.biojava3.core.sequence.location.template.Location;
//import org.jscc.app.client.biojava3.core.sequence.location.template.Point;
//
///**
// * Parser for working with INSDC style locations. This class supports the
// * full range of location types generated by Genbank, INSDC and ENA.
// *
// * @author ayates
// */
//public class InsdcParser {
//
//    private final DataSource dataSource;
//
//    public InsdcParser() {
//        this(DataSource.ENA);
//    }
//
//    public InsdcParser(DataSource dataSource) {
//        this.dataSource = dataSource;
//    }
//
//    public DataSource getDataSource() {
//        return dataSource;
//    }
//
//    /**
//     * Parses a location of the form Accession:1
//     */
//    private static final Pattern SINGLE_LOCATION = Pattern.compile(
//            "\\A ([A-Za-z.0-9]*) :? ([<>]?) (\\d+) \\Z", Pattern.COMMENTS);
//
//    /**
//     * Parses a location of the form Accession:1..4 (also supports the ^
//     * format and undefined locations)
//     */
//    private static final Pattern RANGE_LOCATION = Pattern.compile(
//            "\\A ([A-Za-z.0-9]*) :? ([<>]?) (\\d+) ([.^]+) ([<>]?) (\\d+) \\Z", Pattern.COMMENTS);
//
//    /**
//     * Main method for parsing a location from a String instance
//     *
//     * @param locationString Represents a logical location
//     * @return The parsed location
//     * @throws ParserException thrown in the event of any error during parsing
//     */
//    public Location parse(String locationString) throws ParserException {
//        try {
//            return parse(new StringReader(locationString));
//        }
//        catch(IOException e) {
//            throw new ParserException("Cannot parse the given location '"+
//                    locationString+"'", e);
//        }
//    }
//
//    /**
//     * Reader based version of the parse methods.
//     *
//     * @param reader The source of the data; assumes that end of the reader
//     * stream is the end of the location string to parse
//     * @return The parsed location
//     * @throws IOException Thrown with any reader error
//     * @throws ParserException Thrown with any error with parsing locations
//     */
//    public Location parse(Reader reader) throws IOException, ParserException {
//        List<Location> out = parse(reader, Strand.POSITIVE);
//        if(out.size() > 1) {
//            throw new ParserException("Too many locations parsed "+out);
//        }
//        else if(out.isEmpty()) {
//            throw new ParserException("No locations parsed");
//        }
//        return out.get(0);
//    }
//
//    protected List<Location> parse(Reader reader, Strand strand) throws IOException, ParserException {
//        StringBuilder sb = new StringBuilder();
//        String typeOfJoin = null;
//        List<Location> locationList = new ArrayList<Location>();
//
//        int i = -1;
//        while( (i = reader.read()) != -1 ) {
//            char c = (char)i;
//            switch(c) {
//                case '(':
//                    if(isComplement(sb)) {
//                        locationList.addAll(parse(reader, strand.getReverse()));
//                    }
//                    else {
//                        typeOfJoin = sb.toString();
//                        List<Location> subs = parse(reader, strand);
//                        locationList.add(parseComplex(subs, typeOfJoin));
//                    }
//                    clearStringBuilder(sb);
//                    break;
//                case ',':
//                case ')':
//                    if(sb.length() > 0) {
//                        locationList.add(parseLocation(sb.toString(), strand));
//                    }
//                    if( c == ')') {
//                        return locationList;
//                    }
//                    clearStringBuilder(sb);
//                    break;
//                default:
//                    if(!Character.isWhitespace(c)) {
//                        sb.append(c);
//                    }
//                    break;
//            }
//        }
//
//        if(sb.length() != 0) {
//             locationList.add(parseLocation(sb.toString(), strand));
//             clearStringBuilder(sb);
//        }
//
//        return locationList;
//    }
//
//    private boolean isComplement(StringBuilder sb) {
//        return sb.toString().equals("complement");
//    }
//
//    private void clearStringBuilder(StringBuilder sb) {
//        sb.delete(0, sb.length());
//    }
//
//    protected Location parseComplex(List<Location> subLocations, String type) {
//        if(subLocations.size() == 1) {
//            return subLocations.get(0);
//        }
//        
//        boolean circular = detectCicular(subLocations);
//        Strand strand = detectStrand(subLocations);
//        Point start = detectStart(subLocations);
//        Point end = detectEnd(subLocations, circular);
//        Location l;
//        if("join".equals(type)) {
//            l = new SimpleLocation(start, end, strand, circular, subLocations);
//        }
//        else if("order".equals(type)) {
//            l = new InsdcLocations.OrderLocation(start, end, strand, circular, subLocations);
//        }
//        else if("one-of".equals(type)) {
//            l = new InsdcLocations.OneOfLocation(subLocations);
//        }
//        else if("group".equals(type)) {
//            l = new InsdcLocations.GroupLocation(start, end, strand, circular, subLocations);
//        }
//        else if("bond".equals(type)) {
//            l = new InsdcLocations.BondLocation(subLocations);
//        }
//        else {
//            throw new ParserException("Unknown join type "+type);
//        }
//
//        return l;
//    }
//
//    protected Location parseLocation(String location, Strand strand) {
//        Matcher singleLoc = SINGLE_LOCATION.matcher(location);
//        Matcher rangeLoc = RANGE_LOCATION.matcher(location);
//        if(rangeLoc.matches()) {
//            return parseRange(rangeLoc, strand);
//        }
//        else if(singleLoc.matches()) {
//            return parseSingle(singleLoc, strand);
//        }
//        else {
//            throw new ParserException("Location string does not match "
//                    + "a single or range location");
//        }
//    }
//
//    protected Location parseSingle(Matcher matcher, Strand strand) {
//        String accession = matcher.group(1);
//        String uncertain = matcher.group(2);
//        String location = matcher.group(3);
//        Point p = generatePoint(location, uncertain);
//        if (accession == null || "".equals(accession)) {
//            return new SimpleLocation(p, p, strand);
//        }
//        else {
//            return new SimpleLocation(p, p, strand, getAccession(accession));
//        }
//    }
//
//    protected Location parseRange(Matcher matcher, Strand strand) {
//        String accession = matcher.group(1);
//        String type = matcher.group(4);
//        Point start = generatePoint(
//                matcher.group(3),
//                matcher.group(2));
//        Point end = generatePoint(
//                matcher.group(6),
//                matcher.group(5));
//        boolean betweenBases = "^".equals(type);
//        if (accession == null || "".equals(accession)) {
//            return new SimpleLocation(start, end, strand, false, betweenBases);
//        }
//        else {
//            return new SimpleLocation(start, end, strand, betweenBases, getAccession(accession));
//        }
//    }
//
//    protected Point generatePoint(String locationString, String uncertainString) {
//        int location = Integer.valueOf(locationString);
//        boolean unknown = false;
//        boolean uncertain = (!"".equals(uncertainString));
//        return new SimplePoint(location, unknown, uncertain);
//    }
//
//    protected AccessionID getAccession(String accession) {
//        return new AccessionID(accession, getDataSource());
//    }
//
//    protected boolean detectCicular(List<Location> subLocations) {
//        boolean isCircular = false;
//        int lastMax = 0;
//        for (Location sub : subLocations) {
//            if (sub.getEnd().getPosition() > lastMax) {
//                lastMax = sub.getEnd().getPosition();
//            }
//            else {
//                isCircular = true;
//                break;
//            }
//        }
//        return isCircular;
//    }
//
//    protected Strand detectStrand(List<Location> subLocations) {
//        Strand strand = subLocations.get(0).getStrand();
//        for (Location sub : subLocations) {
//            if (strand != sub.getStrand()) {
//                strand = Strand.UNDEFINED;
//                break;
//            }
//        }
//        return strand;
//    }
//
//    protected Point detectStart(List<Location> subLocations) {
//        return subLocations.get(0).getStart().offset(0); //Easy way to clone
//    }
//
//    protected Point detectEnd(List<Location> subLocations, boolean isCircular) {
//        int end = 0;
//        Point lastPoint = null;
//        if(isCircular) {
//            for (Location sub : subLocations) {
//                lastPoint = sub.getEnd();
//                end += lastPoint.getPosition();
//            }
//        }
//        else {
//            lastPoint = subLocations.get(subLocations.size()-1).getEnd();
//            end = lastPoint.getPosition();
//        }
//        return new SimplePoint(end, lastPoint.isUnknown(), lastPoint.isUncertain());
//    }
//}
